#ifndef YEPXML_TESTWRAPPER_HPP
#define YEPXML_TESTWRAPPER_HPP

#include <iostream>
#include <cstdlib>
#include <set>
#include "YepXML.hpp"

using namespace std;
using namespace yxml;

class Address
{
public:
    Address(){}

    const string& street() const
    {
        return _street;
    }
    void street( const string& value )
    {
        _street = value;
    }

    int number() const
    {
        return _number;
    }

    void number( int value )
    {
        _number = value;
    }

//    class AddressWrapper: public Wrapper
//    {
//    public:
//        AddressWrapper(): Wrapper( "Address" ) {}
    YEPXML_BeginWrapper(Address) // This replace the previous code
//        virtual void recover( Node& tagNode, void* rawObject )
//        {
//            Address& object = *static_cast<Address*>( rawObject );
        YEPXML_BeginRecover(Address)// This replace the previous code
//            for( map<string, string>::const_iterator it = tagNode.attributeBegin(); it != tagNode.attributeEnd(); ++it )
//            {
            YEPXML_BeginAttributesRecover()// This replace the previous code
//                if( it->first == "street" )
//                    object.street( it->second );
//                else
                YEPXML_RecoverAttribute(street)// This replace the previous code
//                if( it->first == "number" )
//                {
//                    int value;
//                    stringstream( it->second ) >> value;
//                    object.number( value );
//                }
                YEPXML_RecoverTypeAttribute(number, int);// This replace the previous code
//            }
            YEPXML_EndAttributesRecover()// This replace the previous code
//        }
        YEPXML_EndRecover()// This replace the previous code

//        virtual void translate( void* rawObject, Node& tagNode )
//        {
//            Address& object = *static_cast<Address*>( rawObject );
//            tagNode.value(tagName());
        YEPXML_BeginTranslate(Address)
//                    tagNode.attribute("street") = object.street();
            YEPXML_TranslateAttribute(street)
//                    tagNode.attribute("number") = dynamic_cast<stringstream&>(stringstream()<< object.number()).str();
            YEPXML_TranslateTypeAttribute(number)
            //
        YEPXML_EndTranslate()
//    };
//    static AddressWrapper wrapper;
    YEPXML_EndWrapper(Address) // This replace the previous code

//    Address( istream& source )
//    {
//        Document doc("");
//        Reader reader(source);
//        reader.read(doc);
//        wrapper.recover(doc, this);
//    }
    YEPXML_DeclareConstructor(Address) // This replace the previous code
//    void save(ostream &out)
//    {
//        Document doc("");
//        wrapper.translate(this, doc);
//        HierarchicalWritter writter(out);
//        writter.write(doc);
//    }
    YEPXML_DeclareSave()
private:
    string _street;
    int _number;
};

// Address::AddressWrapper Address::wrapper;
YEPXML_ImplementWrapperStatic(Address);//This  replace the previous code

class Book
{
public:
    Book(){}

    const string& name() const
    {
        return _name;
    }
    void name( const string& value )
    {
        _name = value;
    }
    const string& author() const
    {
        return _author;
    }
    void author( const string& value )
    {
        _author = value;
    }

    YEPXML_BeginWrapper(Book)
        YEPXML_BeginRecover(Book)
            YEPXML_BeginAttributesRecover()
                YEPXML_RecoverAttribute(name)
                YEPXML_RecoverAttribute(author)
            YEPXML_EndAttributesRecover()
        YEPXML_EndRecover()
        YEPXML_BeginTranslate(Book)
            YEPXML_TranslateAttribute(name)
            YEPXML_TranslateAttribute(author)
        YEPXML_EndTranslate()
    YEPXML_EndWrapper(Book)

    YEPXML_DeclareConstructor(Book)
    YEPXML_DeclareSave()

    bool operator<(const Book &other) const
    {
        return _name < other._name;
    }
private:
    string _name, _author;
};

YEPXML_ImplementWrapperStatic(Book)

class Library
{
public:
    /**
     * @brief Returns the name
     **/
    const string &name( void ) const
    {
        return _name;
    }

    /**
     * @brief Changes the name
     **/
    void name( const string &value )
    {
        _name = value;
    }

    /**
     * @brief Returns the address
     **/
    const Address &address( void ) const
    {
        return _address;
    }

    /**
     * @brief Changes the address
     **/
    void address( const Address &value )
    {
        _address = value;
    }

    void insertBook(const Book &book)
    {
        _books.insert(book);
    }

    void eraseBook(const Book &book)
    {
        _books.erase(book);
    }

    set<Book>::const_iterator booksBegin() const
    {
        return _books.begin();
    }

    set<Book>::const_iterator booksEnd() const
    {
        return _books.end();
    }

    YEPXML_BeginWrapper(Library)
        YEPXML_BeginRecover(Library)
            YEPXML_BeginAttributesRecover()
                YEPXML_RecoverAttribute(name)
            YEPXML_EndAttributesRecover()
            YEPXML_BeginChildrenRecover()
                YEPXML_BeginTagRecover()
                    YEPXML_RecoverTag(Address,address)
                    YEPXML_RecoverTag(Book,insertBook)
                YEPXML_EndTagRecover()
            YEPXML_EndChildrenRecover()
        YEPXML_EndRecover()
        YEPXML_BeginTranslate(Library)
            YEPXML_TranslateAttribute(name)
            YEPXML_TranslateTagUnique(Address, address())
            YEPXML_TranslateTags(Book, booksBegin(), booksEnd())
        YEPXML_EndTranslate()
    YEPXML_EndWrapper(Library)

    YEPXML_DeclareConstructor(Library)
    YEPXML_DeclareSave()

private:
    string _name;
    Address _address;
    set<Book> _books;
};

YEPXML_ImplementWrapperStatic(Library)

inline int testWrapper()
{
    YEPXML_DeclareWrapper(Address);
    YEPXML_DeclareWrapper(Book);
    YEPXML_DeclareWrapper(Library);
    //Testing Address
    string addressRaw = "<?xml version=\"1.0\" standalone=\"yes\" encoding=\"UTF-8\" ?>\n"
                        "<Address street = \"Main Street\" number = \"100\"/>";
    stringstream addressInput( addressRaw );
    Address address( addressInput );
    cout << "Street: " << address.street() << endl;
    cout << "Number: " << address.number() << endl;

    cout << endl;
    address.save(cout);
    cout << endl;

    //Testing Book
    string bookRaw = "<?xml version=\"1.0\" standalone=\"yes\" encoding=\"UTF-8\" ?>\n"
                        "<Book name = \"Lord Of Rings - The Ring Friendship\" author = \"J.R.R Tolkien\"/>";
    stringstream bookInput( bookRaw );
    Book book( bookInput );
    cout << "Name: " << book.name() << endl;
    cout << "Author: " << book.author() << endl;

    cout << endl;
    book.save(cout);
    cout << endl;

    //Testing Library
    string libraryRaw = "<?xml version=\"1.0\" standalone=\"yes\" encoding=\"UTF-8\" ?>\n"
                        "<Library name = \"Simple Direct\">\n"
                        "<Address street = \"Main Street\" number = \"100\"/>\n"
                        "<Book name = \"Lord Of Rings - The Ring Friendship\" author = \"J.R.R Tolkien\"/>\n"
                        "<Book name = \"Lord Of Rings - The Two Towers\" author = \"J.R.R Tolkien\"/>\n"
                        "</Library>";
    stringstream libraryInput( libraryRaw );
    Library library( libraryInput );
    cout << "Name: " << library.name() << endl;
    cout << "Address: " << library.address().street()<<", " << library.address().number() << endl;
    cout << "Books:" << endl;
    for(set<Book>::const_iterator it = library.booksBegin(); it!= library.booksEnd(); ++it)
    {
        cout << "\tName: " << it->name() << endl;
        cout << "\tAuthor: " << it->author() << endl;
        cout << endl;
    }

    cout << endl;
    library.save(cout);
    cout << endl;

    return 0;
}

#endif // YEPXML_TESTWRAPPER_HPP

